package com.spark.lola.service.pur.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.spark.lola.constant.SupOrderAction;
import com.spark.lola.constant.SupOrderFlag;
import com.spark.lola.constant.SupOrderStatus;
import com.spark.lola.entity.pur.*;
import com.spark.lola.entity.sup.SupplierInfo;
import com.spark.lola.feign.LogisticsFeignService;
import com.spark.lola.feign.service.PortalServiceOpenApi;
import com.spark.lola.logistics.entity.LocationInfo;
import com.spark.lola.mapper.gds.ProductInfoMapper;
import com.spark.lola.mapper.pur.*;
import com.spark.lola.mapper.sup.SupplierInfoMapper;
import com.spark.lola.service.gds.IProductInfoService;
import com.spark.lola.service.pur.IOrderInfoService;
import com.spark.lola.service.pur.IOrderProductService;
import com.spark.lola.service.pur.IReturnOrderInfoService;
import com.spark.lola.service.sup.ISupplierInfoService;
import com.spark.lola.support.common.constant.BooleanConstant;
import com.spark.lola.support.common.entity.ShiroUser;
import com.spark.lola.support.common.util.DataBeanUtils;
import com.spark.lola.support.common.util.ResponseBean;
import com.spark.lola.support.common.util.TimeUtil;
import com.spark.lola.support.core.entity.KafkaMessage;
import com.spark.lola.support.core.param.MessageParam;
import com.spark.lola.support.mybatis.service.impl.BaseServiceImpl;
import com.spark.lola.support.wms.cache.CollectionCache;
import com.spark.lola.support.wms.constant.KafkaTopic;
import com.spark.lola.support.wms.constant.OrderType;
import com.spark.lola.support.wms.constant.SmsCodeConstant;
import com.spark.lola.support.wms.constant.SupOrderByType;
import com.spark.lola.support.wms.dto.OrderDto;
import com.spark.lola.support.wms.request.OrderActionRequest;
import com.sun.org.apache.xpath.internal.operations.Or;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import springfox.documentation.spring.web.json.Json;

import java.io.Serializable;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Stream;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author dengyejun
 * @since 2020-04-24
 */
@Service
public class OrderInfoServiceImpl extends BaseServiceImpl<OrderInfoMapper, OrderInfo> implements IOrderInfoService {
    private static final Logger logger = LoggerFactory.getLogger(OrderInfoServiceImpl.class);
    @Value("#{'${wmx.filter-destination-ids}'.split(',')}")
    private List<String> filterDestinationIds;

    @Autowired
    private IOrderProductService iOrderProductService;
    @Autowired
    private OrderProductMapper orderProductMapper;
    @Autowired
    private ISupplierInfoService iSupplierInfoService;
    @Autowired
    private OrderLocationMapper orderLocationMapper;
    @Autowired
    private LogisticsFeignService logisticsFeignService;
    @Autowired
    private OrderProcessMapper orderProcessMapper;
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;
    @Autowired
    private SupplierInfoMapper supplierInfoMapper;
    @Autowired
    private ProductInfoMapper productInfoMapper;
    @Autowired
    private IReturnOrderInfoService returnOrderInfoService;
    @Autowired
    private PortalServiceOpenApi portalServiceOpenApi;
    @Autowired
    private OrderExpensesMapper orderExpensesMapper;

    /**
     * 获取订单全量信息
     */
    @Override
    public OrderInfo findFullById(Long id) {
        OrderInfo model = this.baseMapper.selectById(id);
        if (model == null) {
            return null;
        }
        bindFullOrder(model);
        return model;
    }

    /**
     * 获取订单全量信息
     */
    @Override
    public OrderInfo findFullByNo(String no) {
        OrderInfo model = this.baseMapper.selectByNo(no);
        if (model == null) {
            return null;
        }
        bindFullOrder(model);
        return model;
    }

    /**
     * 创建订单
     * <ol>
     *     <li>1.获取供应商&纸厂信息 设置订单发货/收货基础信息</li>
     *     <li>2.处理订单明细  计算订单供货金额/采购单/货品id链</li>
     *     <li>3.保存订单基础信息&设置明细外键</li>
     *     <li>4.保存订单明细</li>
     *     <li>5.保存发货信息</li>
     *     <li>6.保存收货信息</li>
     *     <li>7.保存订单操作记录</li>
     * </ol>
     *
     * @param form      表单
     * @param shiroUser 用户
     * @return
     * @throws Exception
     */
    @Override
    public OrderInfo add(OrderInfo form, ShiroUser shiroUser) throws Exception {
        logger.info("====创建订单【{}】创建人【{}】===START===", form.getNo(), shiroUser.getLoginNo());
        if (CollectionUtils.isEmpty(form.getDetails())) {
            throw new Exception("请添加货品明细!");
        }
        OrderInfo model = new OrderInfo();
        DataBeanUtils.buildUpdateBean(model, form);
        model.setDetails(form.getDetails());
        logger.info("===获取供应商&纸厂信息...");
        LocationInfo locationInfo = logisticsFeignService.findById(form.getDestinationId());
        SupplierInfo supplierInfo = iSupplierInfoService.getById(model.getSupplierId());
        // 绑定收货人
        model.setConsigneeId(locationInfo.getEmpId());
        model.setCashierId(locationInfo.getCashierId());
        model.setStartArea(supplierInfo.getArea());
        model.setEndArea(locationInfo.getArea());
        model.setSupplierName(supplierInfo.getName());

        logger.info("===处理订单明细...");
        dealProduct(model);
        logger.info("===保存订单基础数据...");
        if (!this.save(model)) {
            throw new Exception("订单保存失败");
        }
        logger.info("===保存货品明细...");
        model.getDetails().forEach(o -> o.setParentId(model.getId()));
        if (!iOrderProductService.saveOrUpdateBatch(model.getDetails(), 100)) {
            throw new Exception("货品明细保存失败");
        }
        logger.info("===保存发货信息...");
        model.setStartLocation(dealStartLoaction(model, supplierInfo));
        logger.info("===保存收货信息...");
        model.setEndLocation(dealEndLoaction(locationInfo, model.getId()));
        logger.info("===保存订单操作记录...");
        dealOrderProcess(model, SupOrderAction.CREATING, shiroUser, null, null);
        logger.info("====创建订单【{}】创建人【{}】===END===", form.getNo(), shiroUser.getLoginNo());
        bindFullOrder(model, false);
        return model;
    }

    /**
     * 修改订单 仅限修改货品及订单基础信息
     *
     * @param form      表单
     * @param shiroUser 用户
     * @return
     * @throws Exception
     */
    @Override
    public OrderInfo update(OrderInfo form, ShiroUser shiroUser) throws Exception {
        logger.info("====修改订单【{}】修改人【{}】===START===", form.getNo(), shiroUser.getLoginNo());
        OrderInfo model = this.findFullById(form.getId());
        if (model.getStatus() > SupOrderStatus.WAIT_PAYMENT) {
            throw new Exception("当前订单已生效，禁止修改");
        }
        if (form.getBankId() != null) {
            model.setBankId(form.getBankId());
        }
        model.setRemark(form.getRemark());
        logger.info("====处理货品明细...");
        // 监听货品明细变化
        dealProductChange(model, form.getDetails());
        model.setDetails(form.getDetails());
        dealProduct(model);
        logger.info("===保存订单基础数据...");
        if (!this.updateById(model)) {
            throw new Exception("订单保存失败");
        }
        logger.info("===保存货品明细...");
        model.getDetails().forEach(o -> o.setParentId(model.getId()));
        if (!iOrderProductService.saveOrUpdateBatch(model.getDetails(), 100)) {
            throw new Exception("货品明细保存失败");
        }
        logger.info("===保存订单操作记录...");
        dealOrderProcess(model, SupOrderAction.UPDATE_BASE, shiroUser, null, null);
        logger.info("====修改订单【{}】创建人【{}】===END===", form.getNo(), shiroUser.getLoginNo());
        bindFullOrder(model);
        return model;
    }

    /**
     * 删除订单
     * <ol>
     *     <li>1.判断状态 是否符合删除条件</li>
     *     <li>2.删除 发货/收货信息</li>
     *     <li>3.删除 订单货品明细</li>
     *     <li>4.删除 订单日志 </li>
     *     <li>5.删除 订单基础数据</li>
     * </ol>
     *
     * @param id
     * @return
     */
    @Override
    public void deleteById(Long id, ShiroUser shiroUser) throws Exception {
        OrderInfo model = this.getById(id);
        logger.info("====删除订单【{}】操作人【{}】===START===", model.getNo(), shiroUser.getLoginNo());
        if (model.getStatus().intValue() > SupOrderStatus.WAIT) {
            throw new Exception("当前订单已生效，禁止修改");
        }
        logger.info("===删除发货/收货信息...");
        if (orderLocationMapper.deleteById(id) == 0) {
            throw new Exception("发货/收货信息删除失败");
        }
        logger.info("===删除订单货品明细...");
        QueryWrapper<OrderProduct> queryOrderProduct = new QueryWrapper<>();
        queryOrderProduct.eq("parent_id", id);
        if (!iOrderProductService.remove(queryOrderProduct)) {
            throw new Exception("订单日志删除失败");
        }
        logger.info("===删除订单日志...");
        QueryWrapper<OrderProcess> queryOrderProcess = new QueryWrapper<>();
        queryOrderProcess.eq("order_id", id);
        if (orderProcessMapper.delete(queryOrderProcess) == 0) {
            throw new Exception("订单日志删除失败");
        }
        logger.info("===删除订单基础信息...");
        if (!this.removeById(id)) {
            throw new Exception("订单基础信息删除失败");
        }
        logger.info("====删除订单【{}】操作人【{}】===END===", model.getNo(), shiroUser.getLoginNo());
    }


    /**
     * 获取订单记录
     *
     * @param id
     * @return
     */
    @Override
    public List<OrderProcess> logs(Long id) {
        QueryWrapper<OrderProcess> query = new QueryWrapper<>();
        query.eq("order_id", id);
        query.orderByAsc("create_time");
        return orderProcessMapper.selectList(query);
    }

    /**
     * 驳回订单处理
     * <ol>
     *     <li>1.判断订单状态是否满足驳回条件</li>
     *     <li>2.更新订单状态</li>
     *     <li>3.添加操作记录</li>
     * </ol>
     *
     * @param shiroUser
     * @throws Exception
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reject(KafkaMessage kafkaMessage, OrderActionRequest request, ShiroUser shiroUser) throws Exception {

        OrderInfo model = this.getById(request.getId());
        logger.info("====驳回订单【{}】当前状态【{}】 操作人【{}】===START===", model.getNo(), SupOrderStatus.getTxt(model.getStatus()), shiroUser.getLoginNo());
        // 调整后状态
        int oldStatus = model.getStatus().intValue();
        int newStatus = oldStatus - 1;
        if (oldStatus > SupOrderStatus.COMPLETED
                || newStatus < SupOrderStatus.WAIT) {
            throw new Exception("当前订单不满足驳回条件，禁止操作!");
        }
        logger.info("===更新订单状态 【{}】=>【{}】", model.getStatus(), newStatus);
        model.setStatus(newStatus);
        model.setFlag(SupOrderFlag.REJECT);
        if (model.getStatus().equals(SupOrderStatus.WAIT_PRICING)) {
            // 重置定价时间
            model.setPricingTime(null);
        }
        if(oldStatus == SupOrderStatus.WAIT_ADJUST){
            orderExpensesMapper.deleteByNo(model.getNo());
        }
        if (!updateById(model)) {
            throw new Exception("订单更新失败");
        }

        // 是否回滚至待定价状态
        if (model.getStatus().equals(SupOrderStatus.WAIT_PRICING)) {
            // 执行回滚业务
            dealRollbackPricing(kafkaMessage, model);
        }
        request.getParams().put("oldStatus", SupOrderStatus.getTxt(oldStatus));
        request.getParams().put("newStatus", SupOrderStatus.getTxt(newStatus));
        String actionMsg = dealOrderProcess(model, SupOrderAction.REJECT, shiroUser, request.getMessage(), JSON.toJSONString(request.getParams()));
        // kafka推送
        sendOrderChange(model, shiroUser.getRealName() + "驳回了采购单", actionMsg);

        logger.info("====驳回订单【{}】更新状态【{}】 操作人【{}】===START===", model.getNo(), SupOrderStatus.getTxt(model.getStatus()), shiroUser.getLoginNo());
    }

    /**
     * 订单发货
     * <ol>
     *     <li>1.校验订单状态&操作人权限</li>
     *     <li>2.更新订单信息</li>
     *     <li>3.保存订单操作记录</li>
     *     <li>4.订单消息推送</li>
     * </ol>
     */
    @Override
    public void checking(OrderActionRequest request, ShiroUser shiroUser) throws Exception {
        OrderInfo model = this.getById(request.getId());
        logger.info("====订单发货【{}】操作人【{}】===START===", model.getNo(), shiroUser.getLoginNo());
        if (!model.getStatus().equals(SupOrderStatus.WAIT)) {
            throw new Exception("非法操作");
        }
//        if (model.getSalesmanId().equals(shiroUser.getId())) {
//            throw new Exception("您不是当前订单发货人,禁止操作!");
//        }
        String license = request.getParams().get("license").toString();
        if(StringUtils.isEmpty(license)){
            throw new Exception("未提供车牌号");
        }
        if(!StringUtils.isEmpty(request.getMessage())){
           model.setRemark(request.getMessage());
        }
        model.setStatus(SupOrderStatus.WAIT_ECEIVING);
        model.setFlag(request.getFlag());
        model.setLicense(license);
        logger.info("===更新订单状态...");
        if (!updateById(model)) {
            throw new Exception("订单更新失败");
        }
        logger.info("===保存操作记录...");
        String extendJson = JSON.toJSONString(request.getParams());
        String actionMsg = dealOrderProcess(model, SupOrderAction.CHECKING, shiroUser, request.getMessage(), extendJson);
        // kafka推送
        sendOrderChange(model, "采购单已发货！", actionMsg);
        logger.info("====订单发货【{}】操作人【{}】===END===", model.getNo(), shiroUser.getLoginNo());
    }

    /**
     * 订单收货
     */
    @Override
    public void eceiving(OrderActionRequest request, ShiroUser shiroUser) throws Exception {
        OrderInfo model = this.getById(request.getId());
        logger.info("====订单收货【{}】操作人【{}】===START===", model.getNo(), shiroUser.getLoginNo());
        if (!model.getStatus().equals(SupOrderStatus.WAIT_ECEIVING)) {
            throw new Exception("非法操作");
        }
        if (model.getSettleQuantity().compareTo(BigDecimal.ZERO) < 1) {
            // 小于等于0 非法操作
            throw new Exception("未填写货品验收量!");
        }
//        if (model.getConsigneeId().equals(shiroUser.getId())) {
//            throw new Exception("您不是当前订单收货人,禁止操作!");
//        }
        // model.setFlag(request.getFlag());
        // 获取扣点总计
        request.getParams().put("deductQuantity",model.getDeductQuantity());
        request.getParams().put("deduct",model.getDeduct());
        Integer eceiveType = Integer.parseInt(request.getParams().get("type").toString());
        if (eceiveType.equals(BooleanConstant.TRUE)) {
            // 部分收货
            model.setFlag(SupOrderFlag.PART_RETURN);
        }
        model.setStatus(SupOrderStatus.WAIT_PRICING);
        logger.info("===更新订单状态...");
        if (!updateById(model)) {
            throw new Exception("订单更新失败");
        }
        logger.info("===保存操作记录...");
        String extendJson = JSON.toJSONString(request.getParams());
        String info = dealOrderProcess(model, SupOrderAction.ECEIVING, shiroUser, request.getMessage(), extendJson);
        // kafka推送
        sendOrderChange(model, "采购单已收货！", info);
        logger.info("====订单收货【{}】操作人【{}】===END===", model.getNo(), shiroUser.getLoginNo());
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void pricing(KafkaMessage kafkaMessage, OrderActionRequest request, ShiroUser shiroUser) throws Exception {
        OrderInfo model = this.getById(request.getId());
        logger.info("====订单定价【{}】操作人【{}】===START===", model.getNo(), shiroUser.getLoginNo());
        if (!model.getStatus().equals(SupOrderStatus.WAIT_PRICING)) {
            throw new Exception("非法操作");
        }
        if(model.getTotalAmount().compareTo(BigDecimal.ZERO) < 1){
            throw new Exception("请保存货品单价后进行定价确认!");
        }
        // 获取定价金额
//        BigDecimal actualAmount = new BigDecimal(request.getParams().get("actualAmount").toString());
//        if (actualAmount.compareTo(BigDecimal.ZERO) < 1) {
//            throw new Exception("非法金额数值");
//        }
//        model.setActualAmount(actualAmount);
        model.setStatus(SupOrderStatus.WAIT_ADJUST);
        model.setFlag(request.getFlag());
        model.setPricingTime(new Date());
        logger.info("===保存支出单...");
        // 获取支付金额
        BigDecimal paymentAmount = dealExpenses(model,request.getParams());
        model.setPaymentAmount(paymentAmount);
        logger.info("===更新订单状态...");
        if (!updateById(model)) {
            throw new Exception("订单更新失败");
        }
        logger.info("===订单定价业务处理...");
        dealUpdatePricing(kafkaMessage, model);

        logger.info("===保存操作记录...");
        String actionMsg = dealOrderProcess(model, SupOrderAction.PRICING, shiroUser, request.getMessage(), JSON.toJSONString(request.getParams()));
        // kafka推送
        sendOrderChange(model, shiroUser.getRealName() + "定价了采购单", actionMsg);
        logger.info("====订单定价【{}】金额【{}】操作人【{}】===END===", model.getNo(), model.getActualAmount(), shiroUser.getLoginNo());
    }

    /**
     * 核算金额
     */
    @Override
    public void adjusting(OrderActionRequest request, ShiroUser shiroUser) throws Exception {
        OrderInfo model = this.getById(request.getId());
        logger.info("====订单核算金额【{}】操作人【{}】===START===", model.getNo(), shiroUser.getLoginNo());
        if (!model.getStatus().equals(SupOrderStatus.WAIT_ADJUST)) {
            throw new Exception("非法操作");
        }
        // 获取定价金额
//        BigDecimal actualAmount = new BigDecimal(request.getParams().get("actualAmount").toString());
//        if (actualAmount.compareTo(BigDecimal.ZERO) < 1) {
//            throw new Exception("非法金额数值");
//        }
        // 保存历史定价
        //request.getParams().put("pricing", model.getActualAmount().toString());
        logger.info("===" + request.getMessage());
        //model.setActualAmount(actualAmount);
        model.setStatus(SupOrderStatus.WAIT_REPLY);
        // model.setFlag(request.getFlag());

        logger.info("===更新订单状态...");
        if (!updateById(model)) {
            throw new Exception("订单更新失败");
        }
        logger.info("===保存操作记录...");
        String actionMsg = dealOrderProcess(model, SupOrderAction.ADJUSTING, shiroUser, request.getMessage(), JSON.toJSONString(request.getParams()));
        // kafka推送
        sendOrderChange(model, shiroUser.getRealName() + "核算了采购单", actionMsg);
        logger.info("====订单核算金额【{}】金额【{}】操作人【{}】===END===", model.getNo(), model.getPaymentAmount(), shiroUser.getLoginNo());
    }

    /**
     * 核算审核
     * @param request
     * @param shiroUser
     * @throws Exception
     */
    @Override
    public void replying(OrderActionRequest request, ShiroUser shiroUser) throws Exception {
        OrderInfo model = this.getById(request.getId());
        logger.info("====订单核算审批【{}】操作人【{}】===START===", model.getNo(), shiroUser.getLoginNo());
        if (!model.getStatus().equals(SupOrderStatus.WAIT_REPLY)) {
            throw new Exception("非法操作");
        }
        logger.info("支付类型【{}】",request.getFlag());
        logger.info("===" + request.getMessage());
        model.setStatus(SupOrderStatus.WAIT_PAYMENT);
        model.setFlag(request.getFlag());

        logger.info("===更新订单状态...");
        if (!updateById(model)) {
            throw new Exception("订单更新失败");
        }
        logger.info("===保存操作记录...");
        String actionMsg = dealOrderProcess(model, SupOrderAction.REPLYING, shiroUser, request.getMessage(), JSON.toJSONString(request.getParams()));
        // kafka推送
        sendOrderChange(model, shiroUser.getRealName() + "核算了采购单", actionMsg);
        logger.info("====订单核算审批【{}】金额【{}】操作人【{}】===END===", model.getNo(), model.getActualAmount(), shiroUser.getLoginNo());
    }

    /**
     * 订单结算
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void paying(KafkaMessage kafkaMessage, OrderActionRequest request, ShiroUser shiroUser) throws Exception {
        OrderInfo model = this.getById(request.getId());
        logger.info("====订单结算【{}】操作人【{}】===START===", model.getNo(), shiroUser.getLoginNo());
        if (!model.getStatus().equals(SupOrderStatus.WAIT_PAYMENT)) {
            throw new Exception("非法操作");
        }
//        if (model.getCashierId().equals(shiroUser.getId())) {
//            throw new Exception("您不是当前订单收货人,禁止操作!");
//        }
        // 获取实付金额
        BigDecimal paymentAmount = new BigDecimal(request.getParams().get("paymentAmount").toString());
        if(request.getParams().get("bankId")==null){
            throw new Exception("缺少关键参数!");
        }
        if (paymentAmount.compareTo(BigDecimal.ZERO) < 1) {
            throw new Exception("非法金额数值");
        }
        model.setPaymentAmount(paymentAmount);
        model.setStatus(SupOrderStatus.COMPLETED);
        model.setFlag(request.getFlag());
        logger.info("更新订单状态...");
        if (!updateById(model)) {
            throw new Exception("订单更新失败");
        }
        logger.info("更新供应商销售统计...");
        dealSupplierPayment(kafkaMessage, model);
        logger.info("===保存操作记录...");
        String extendJson = JSON.toJSONString(request.getParams());
        String actionMsg = dealOrderProcess(model, SupOrderAction.PAYING, shiroUser, request.getMessage(), extendJson);
        // kafka推送
        sendOrderChange(model, shiroUser.getRealName() + "结算了采购单", actionMsg);
        sendCompletedMessage(model);
        logger.info("====订单结算【{}】操作人【{}】===END===", model.getNo(), shiroUser.getLoginNo());
    }

    /**
     * 订单回收
     * @param orderId
     * @param shiroUser
     */
    @Override
    public void recycle(Long orderId, ShiroUser shiroUser) throws Exception {
        OrderInfo model = this.getById(orderId);
        logger.info("====订单回收【{}】操作人【{}】===START===", model.getNo(), shiroUser.getLoginNo());
        if (!model.getStatus().equals(SupOrderStatus.RETURN)) {
            throw new Exception("非法操作");
        }

        if(TimeUtil.diffByDay(model.getUpdateTime(),new Date())<2) {
            throw new Exception("当前操作需已退货超过48小时！");
        }
        model.setStatus(SupOrderStatus.DUSTBIN);

        logger.info("===更新订单状态...");
        if (!updateById(model)) {
            throw new Exception("订单更新失败");
        }
        logger.info("===保存操作记录...");
        String actionMsg = dealOrderProcess(model, SupOrderAction.RECYCLE, shiroUser, "", null);

        logger.info("====订单回收【{}】操作人【{}】===START===", model.getNo(), shiroUser.getLoginNo());
    }

    private void sendCompletedMessage(OrderInfo model) {
        if(filterDestinationIds.contains(model.getDestinationId().toString())){
            logger.warn("过滤当前纸厂【{}-{}】短信通知",model.getDestination(),model.getDestinationId());
            return;
        }
        ShiroUser salesman = CollectionCache.employees.get(model.getSalesmanId());
        SupplierInfo supplierInfo = supplierInfoMapper.selectById(model.getSupplierId());
        // 获取收货时间
        Date consigneeDate = orderProcessMapper.getTimeByAction(model.getId(),SupOrderAction.ECEIVING);
        // 获取收货信息
        model.setEndLocation( orderLocationMapper.selectByOrderId(1,model.getId()));
        String message = "【华荣纸联】尊敬的客户：订单" + model.getNo() +
                "已完成打款：打款金额" + model.getPaymentAmount() +
                "元，扣点" + model.getDeductQuantity() +
                "吨，结算吨数：" + model.getSettleQuantity() +
                "吨，车牌号："+ model.getLicense()+
                "，收货日期："+new SimpleDateFormat("MM月dd日 HH:mm:ss").format(consigneeDate)+
                "，纸厂："+model.getEndLocation().getName()+
                "，请注意查收。";
        Map<String, String> map = new HashMap<>();
        map.put("message", message);

        MessageParam param = new MessageParam(SmsCodeConstant.OTHER_CODE, "+86" + salesman.getPhone(), map, null);
        param.getMobiles().add("+86" + supplierInfo.getMobile());
        ResponseBean responseBean = portalServiceOpenApi.sendMessage(param);
        if (responseBean.getCode() != 200) {
            logger.error("短信发送失败!{}", JSON.toJSONString(param));
        }
    }

    /**
     * 取消订单
     */
    @Override
    public void canceling(OrderActionRequest request, ShiroUser shiroUser) throws Exception {
        OrderInfo model = this.getById(request.getId());
        logger.info("====取消订单【{}】操作人【{}】===START===", model.getNo(), shiroUser.getLoginNo());
        if (model.getStatus().intValue() > SupOrderStatus.WAIT_PAYMENT) {
            throw new Exception("非法操作");
        }
        model.setStatus(SupOrderStatus.CANCELED);
        model.setFlag(request.getFlag());

        logger.info("===更新订单状态...");
        if (!updateById(model)) {
            throw new Exception("订单更新失败");
        }
        logger.info("===保存操作记录...");
        String actionMsg = dealOrderProcess(model, SupOrderAction.CANCELING, shiroUser, request.getMessage(), null);
        // kafka推送
        sendOrderChange(model, shiroUser.getRealName() + "取消了采购单", actionMsg);
        logger.info("====取消订单【{}】操作人【{}】===END===", model.getNo(), shiroUser.getLoginNo());
    }

    /**
     * 订单退货
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long returning(KafkaMessage kafkaMessage, OrderActionRequest request, ShiroUser shiroUser) throws Exception {
        OrderInfo model = this.findFullById(request.getId());
        logger.info("====订单退货【{}】操作人【{}】===START===", model.getNo(), shiroUser.getLoginNo());
        if (model.getStatus().intValue() > SupOrderStatus.WAIT_ECEIVING) {
            throw new Exception("非法操作");
        }
        Integer returnType = new Integer(request.getParams().get("type").toString());
        String actionMsg = "";
        String extendJson = JSON.toJSONString(request.getParams());

        // 撤销订单标签
        model.setFlag(BooleanConstant.FALSE);
        if (returnType.equals(BooleanConstant.FALSE)) {
            if (model.getSettleQuantity().compareTo(BigDecimal.ZERO) == 1) {
                throw new Exception("存在收货结算,请调整货品收货信息!");
            }
            model.setStatus(SupOrderStatus.RETURN);
            logger.info("===保存操作记录...");
            actionMsg = dealOrderProcess(model, SupOrderAction.RETURNING, shiroUser, request.getMessage(), extendJson);
        } else {
            if (model.getSettleQuantity().compareTo(BigDecimal.ZERO) == 0) {
                throw new Exception("请进行收货结算,再进行部分退货!");
            }
            model.setStatus(SupOrderFlag.PART_RETURN);
            actionMsg = dealOrderProcess(model, SupOrderAction.PART_RETURNING, shiroUser, request.getMessage(), extendJson);
        }
        Long returnId = returnOrderInfoService.add(model, shiroUser);

        logger.info("===更新订单状态...");
        if (!updateById(model)) {
            throw new Exception("订单更新失败");
        }
        // kafka推送
        bindOrderCalMessage(kafkaMessage, model, TimeUtil.getCurrentTime());
        sendOrderChange(model, shiroUser.getRealName() + "进行了退货操作", actionMsg);
        logger.info("====订单退货【{}】操作人【{}】===END===", model.getNo(), shiroUser.getLoginNo());
        return returnId;
    }

    /**
     * 添加发货信息
     *
     * @param model
     * @return
     */
    private OrderLocation dealStartLoaction(OrderInfo model, SupplierInfo supplierInfo) throws Exception {

        OrderLocation start = new OrderLocation();
        start.setLinkman(supplierInfo.getLinkman());
        start.setMobile(supplierInfo.getMobile());
        start.setAreaIds(supplierInfo.getAreaIds());
        start.setArea(supplierInfo.getArea());
        start.setAddress(supplierInfo.getAddress());
        start.setMode(0);
        start.setParentId(model.getId());
        start.setName(supplierInfo.getName());
        if (orderLocationMapper.insert(start) == 0) {
            throw new Exception("发货信息保存失败");
        }
        return start;
    }

    /**
     * 处理收货人信息
     *
     * @param locationInfo
     * @return
     */
    private OrderLocation dealEndLoaction(LocationInfo locationInfo, Long orderId) throws Exception {
        OrderLocation end = new OrderLocation();
        end.setLinkman(locationInfo.getLinkman());
        end.setMobile(locationInfo.getMobile());
        end.setAreaIds(locationInfo.getAreaIds());
        end.setArea(locationInfo.getArea());
        end.setAddress(locationInfo.getAddress());
        end.setMode(1);
        end.setParentId(orderId);
        end.setName(locationInfo.getName());
        if (orderLocationMapper.insert(end) == 0) {
            throw new Exception("发货信息保存失败");
        }
        return end;
    }

    /**
     * 更新发货/收货信息
     *
     * @param form
     * @return
     */
    @Override
    public boolean updateLoaction(OrderLocation form) {
        OrderLocation model = orderLocationMapper.selectByOrderId(form.getMode(), form.getParentId());
        DataBeanUtils.buildUpdateBean(model, form);
        UpdateWrapper<OrderLocation> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("mode", form.getMode());
        updateWrapper.eq("parent_id", form.getParentId());
        if (form.getMode().equals(0)) {
            this.baseMapper.updateStartArea(form.getParentId(), form.getArea());
        } else {
            this.baseMapper.updateEndArea(form.getParentId(), form.getArea());
        }
        return orderLocationMapper.update(model, updateWrapper) > 0;
    }

    @Override
    public OrderProcess getOrderProcess(Long orderId, Integer status) {
        QueryWrapper<OrderProcess> query = new QueryWrapper<>();
        query.eq("order_id", orderId);
        query.eq("flag", BooleanConstant.TRUE);
        query.eq("action", status);
        List<OrderProcess> lst = orderProcessMapper.selectList(query);
        if (lst.size() > 0) {
            return lst.get(0);
        }
        return null;
    }

    @Override
    public Map<String, BigDecimal> statistics(Integer type, Long byId, String start, String end) {
        Map<String, BigDecimal> map = new HashMap<>();
        List<Map<String, Object>> sales = baseMapper.getSales(type, byId, start, end);
        // 获取已付款金额
        BigDecimal orderPayment = baseMapper.getPaymentByStatus(type, byId, start, end);
        // 获取退货单数
        Integer returnCount = baseMapper.getReturnCount(type, byId, start, end);
        if (!CollectionUtils.isEmpty(sales) && sales.get(0) != null) {
            sales.get(0).keySet().forEach(key -> {
                map.put(key, new BigDecimal(sales.get(0).get(key).toString()));
            });
            map.put("orderPayment", orderPayment);
            map.put("returnCount", new BigDecimal(returnCount));
        }
        return map;
    }

    @Override
    public BigDecimal waitingPayment(Integer type, Long byId, List<String> salesmanIds, String start, String end) {
        return baseMapper.getWaitingPaymentByStatus(type, byId, salesmanIds.toArray(),start,end);
    }

    @Override
    public BigDecimal waitingPayment(Integer type, Long byId, List<String> salesmanIds) {
        return baseMapper.getWaitingPaymentByStatus(type, byId, salesmanIds.toArray(),null,null);
    }

    @Override
    public BigDecimal supplierWaitingPayment(Long supplierId, String start, String end) {
        return baseMapper.getWaitingPaymentByStatus(0, supplierId, null,start,end);
    }

    @Override
    public BigDecimal supplierWaitingPayment(Long supplierId) {
        return baseMapper.getWaitingPaymentByStatus(0, supplierId, null,null,null);
    }

    @Override
    public Map<Integer, Integer> orderCountByStatus(Integer type, Long byId, List<String> salesmanIds) {
        Map<Integer, Integer> map = new HashMap<>();
        List<Map<String, Object>> orderCount = baseMapper.getCountStatus(type, byId, salesmanIds.toArray());
        if (!CollectionUtils.isEmpty(orderCount) && orderCount.get(0) != null) {
            orderCount.forEach(o -> {
                Integer status = Integer.parseInt(o.get("status").toString());
                Integer count = Integer.parseInt(o.get("order_count").toString());
                map.put(status, count);
            });
        }
        if (!map.containsKey(SupOrderStatus.WAIT)) {
            map.put(SupOrderStatus.WAIT, 0);
        }
        if (!map.containsKey(SupOrderStatus.WAIT_ECEIVING)) {
            map.put(SupOrderStatus.WAIT_ECEIVING, 0);
        }
        if (!map.containsKey(SupOrderStatus.WAIT_PRICING)) {
            map.put(SupOrderStatus.WAIT_PRICING, 0);
        }
        if (!map.containsKey(SupOrderStatus.WAIT_ADJUST)) {
            map.put(SupOrderStatus.WAIT_ADJUST, 0);
        }
        if (!map.containsKey(SupOrderStatus.WAIT_PAYMENT)) {
            map.put(SupOrderStatus.WAIT_PAYMENT, 0);
        }
        if(!map.containsKey(SupOrderStatus.WAIT_REPLY)){
            map.put(SupOrderStatus.WAIT_REPLY,0);
        }
        return map;
    }


    /**
     * 处理订单明细
     * @param orderInfo
     */
    private void dealProduct(OrderInfo orderInfo) throws Exception {
        logger.info("===开始处理货品明细");
        // 订单采购量
        BigDecimal quantity = BigDecimal.ZERO;
        // 结算量
        BigDecimal settelQuantity = BigDecimal.ZERO;
        // 采购总金额
        BigDecimal totalAmount = BigDecimal.ZERO;
        // 结算金额
        BigDecimal actualAmount = BigDecimal.ZERO;

        // 扣点
        double deduct = 0d;
        // 打包数
        int packages = 0;
        // 货品id链
        List<Long> productIds = new ArrayList<>();
        List<String> productNames = new ArrayList<>();
        // 处理货品明细
        for (OrderProduct item : orderInfo.getDetails()) {
            if (item.getProductId() == null
                    || StringUtils.isEmpty(item.getProductNo())) {
                throw new Exception("货品缺少参数!");
            }
            // 计算小计
            item.setMoney(item.getQuantity().multiply(item.getPrice()).setScale(2, BigDecimal.ROUND_HALF_UP));
            // 计算采购量&结算量
            quantity = quantity.add(item.getQuantity());
            // 获取扣点重量
            if(item.getDeduct().intValue()>0) {
                BigDecimal deductQuantity = item.getQuantity().multiply(item.getDeduct())
                        .divide(new BigDecimal(100), 3, BigDecimal.ROUND_HALF_UP);
                item.setSettleQuantity(
                        item.getQuantity().subtract(deductQuantity)
                );
                item.setDeductQuantity(deductQuantity);
            }
            // 计算结算金额
            item.setSettleMoney(item.getSettleQuantity().multiply(item.getPrice()).setScale(2, BigDecimal.ROUND_HALF_UP));

            settelQuantity = settelQuantity.add(item.getSettleQuantity());
            // 计算金额 & 结算金额
            totalAmount = totalAmount.add(item.getMoney());
            actualAmount = actualAmount.add(item.getSettleMoney());
            productIds.add(item.getProductId());
            productNames.add(item.getName() + "," + item.getPackages() + "包," + item.getQuantity() + "吨");
            packages += item.getPackages();
        }
        // 更新订单信息
        orderInfo.setQuantity(quantity.setScale(3, BigDecimal.ROUND_HALF_UP));
        orderInfo.setSettleQuantity(settelQuantity.setScale(3, BigDecimal.ROUND_HALF_UP));
        // 获取扣点  (采购量-结算量)/采购量
        if(orderInfo.getSettleQuantity().compareTo(BigDecimal.ZERO)==1) {
            BigDecimal orderDeduct =orderInfo.getQuantity()
                    .subtract(orderInfo.getSettleQuantity())
                    .divide(orderInfo.getQuantity(), 8, BigDecimal.ROUND_HALF_UP)
                    .multiply(new BigDecimal(100));
            logger.info("订单【{}】扣点率【{}】",orderInfo.getNo(), orderDeduct);
            orderInfo.setDeduct(
                    orderDeduct
                            .setScale(3,BigDecimal.ROUND_HALF_UP)
            );
        }
        orderInfo.setTotalAmount(totalAmount.setScale(2, BigDecimal.ROUND_HALF_UP));
        orderInfo.setActualAmount(actualAmount.setScale(2, BigDecimal.ROUND_HALF_UP));
        orderInfo.setProductIds(StringUtils.join(productIds.toArray(), ","));
        orderInfo.setProductNames(StringUtils.join(productNames.toArray(), "|"));
        orderInfo.setPackages(packages);
        if (orderInfo.getSettleQuantity().compareTo(BigDecimal.ZERO) == 1) {
            // 计算扣点
            orderInfo.setDeductQuantity(orderInfo.getQuantity().subtract(orderInfo.getSettleQuantity()));
        }
        logger.info("===采购量【{}】采购总额【{}】货品链【{}】", orderInfo.getQuantity(), orderInfo.getTotalAmount(), orderInfo.getProductIds());
    }

    /**
     * 处理货品变化
     * @param orderInfo 订单
     * @param products  最新明细列表
     */
    private void dealProductChange(OrderInfo orderInfo, List<OrderProduct> products) {
        for (OrderProduct old : orderInfo.getDetails()) {
            // 判断历史明细是否存在  删除
            Stream<OrderProduct> stream = products.stream().filter(o -> o.getId() != null && o.getId().equals(old.getId()));
            if (stream.count() == 0) {
                iOrderProductService.removeById(old.getId());
            }
        }
    }

    public static void main(String[] args) {
        BigDecimal quantity = new BigDecimal(25.63);
        BigDecimal settleQuantity = new BigDecimal(24.124);
        BigDecimal orderDeduct = quantity
                .subtract(settleQuantity)
                .divide(quantity, 8, BigDecimal.ROUND_HALF_UP)
                .multiply(new BigDecimal(100));
        System.out.println(orderDeduct);
    }

    /**
     * 保存订单操作记录
     *
     * @param orderInfo
     * @param action
     * @param shiroUser
     * @param message
     * @param extendJson
     * @return
     * @throws Exception
     */
    private String dealOrderProcess(OrderInfo orderInfo, Integer action, ShiroUser shiroUser, String message, String extendJson) throws Exception {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dealTime = formatter.format(new Date());
        String info = String.format("【%s】%s 进行【%s】操作", shiroUser.getRealName(), dealTime, SupOrderAction.getTxt(action));
        OrderProcess orderProcess = new OrderProcess(shiroUser, orderInfo, action, info, message, extendJson);
        // 重置历史状态
        orderProcessMapper.resetFlag(orderInfo.getId(), action);
        if (orderProcessMapper.insert(orderProcess) == 0) {
            throw new Exception("订单记录保存失败");
        }
        return JSON.toJSONString(orderProcess);
    }

    private void bindFullOrder(OrderInfo model) {
        bindFullOrder(model, true);
    }

    private void bindFullOrder(OrderInfo model, boolean lazy) {
        if (lazy) {
            // 绑定发货信息
            model.setStartLocation(orderLocationMapper.selectByOrderId(0, model.getId()));
            // 绑定收货信息
            model.setEndLocation(orderLocationMapper.selectByOrderId(1, model.getId()));
            // 绑定订单明细
            QueryWrapper<OrderProduct> query = new QueryWrapper<>();
            query.eq("parent_id", model.getId());
            List<OrderProduct> lst = iOrderProductService.list(query);
            if (!CollectionUtils.isEmpty(lst)) {
                model.setDetails(lst);
            }
        }
        // 绑定业务员信息
        model.setSalesman(CollectionCache.employees.get(model.getSalesmanId()));
        // 绑定收货人信息
        model.setConsignee(CollectionCache.employees.get(model.getConsigneeId()));
        // 绑定出纳信息
        if (model.getCashierId() != null) {
            model.setCashier(CollectionCache.employees.get(model.getCashierId()));
        }

    }

    private void sendOrderChange(OrderInfo orderInfo, String message, String msg) {
        logger.info("===推送订单...");
        OrderDto<OrderInfo> param = new OrderDto<>(OrderType.PUR_ORDER, orderInfo, msg, message);
        kafkaTemplate.send(KafkaTopic.PUR_ORDER_CHANGE, orderInfo.getId().toString(), JSON.toJSONString(param));
    }

    /**
     * 订单定价 业务处理
     * <ol>
     *     <li>1.更新货品明细验收日期</li>
     *     <li>2.更新货品统计数据</li>
     *     <li>3.发送货品日统计更新通知</li>
     *     <li>4.更新供应商统计数据</li>
     *     <li>5.发送供应商日统计更新通知</li>
     * </ol>
     *
     * @param orderInfo
     */
    private void dealUpdatePricing(KafkaMessage kafkaMessage, OrderInfo orderInfo) {
        logger.info("===更新货品明细验收日期");
        orderProductMapper.pricing(orderInfo.getId());
        logger.info("===更新货品统计&发送日统计更新通知");
        String pricingTime = dealProductPricingChange(kafkaMessage, orderInfo, BooleanConstant.TRUE);
        logger.info("===更新供应商统计数据&发送日统计更新通知");
        supplierInfoMapper.updateSales(orderInfo.getActualAmount(), orderInfo.getSettleQuantity(), orderInfo.getSupplierId());
        kafkaMessage.add(KafkaTopic.STATISTICS_CAL_SUPPLIER, orderInfo.getSupplierId().toString(), pricingTime);
        kafkaMessage.add(KafkaTopic.STATISTICS_CAL_SALESMAN, orderInfo.getSalesmanId().toString(), pricingTime);
        kafkaMessage.add(KafkaTopic.STATISTICS_CAL_DESTINATION, orderInfo.getDestinationId().toString(), pricingTime);
    }

    private void dealRollbackPricing(KafkaMessage kafkaMessage, OrderInfo orderInfo) {
        logger.info("===回滚货品统计&发送日统计更新通知");
        String pricingTime = dealProductPricingChange(kafkaMessage, orderInfo, BooleanConstant.FALSE);
        logger.info("===重置货品明细验收日期");
        orderProductMapper.ressetPricing(orderInfo.getId());
        logger.info("===回滚供应商统计数据&发送日统计更新通知");
        supplierInfoMapper.updateSales(orderInfo.getActualAmount().negate(), orderInfo.getSettleQuantity().negate(), orderInfo.getSupplierId());

        //拼装kafka消息
        bindOrderCalMessage(kafkaMessage, orderInfo, pricingTime);
    }

    private String dealProductPricingChange(KafkaMessage kafkaMessage, OrderInfo orderInfo, Integer mode) {
        List<OrderProduct> products = orderProductMapper.findByOrderId(orderInfo.getId());
        String pricingTime = TimeUtil.getCurrentTime();
        for (OrderProduct product : products) {
            if (mode == BooleanConstant.TRUE) {
                // 更新货品统计
                productInfoMapper.updateSales(product.getSettleMoney(), product.getSettleQuantity(), product.getProductId());
            } else {
                // 更新货品统计
                productInfoMapper.updateSales(product.getSettleMoney().negate(), product.getSettleQuantity().negate(), product.getProductId());
                pricingTime = TimeUtil.getTime(product.getPricingTime());
            }
            // 推送货品日统计更新通知
            kafkaMessage.add(KafkaTopic.STATISTICS_CAL_PRODCUT, product.getProductId().toString(), pricingTime);
        }
        return pricingTime;
    }

    private void dealSupplierPayment(KafkaMessage kafkaMessage, OrderInfo orderInfo) throws Exception {
        if (supplierInfoMapper.updateOrderPayment(orderInfo.getSupplierId(), orderInfo.getActualAmount()) == 0) {
            throw new Exception("供应商统计更新失败");
        }
        bindOrderCalMessage(kafkaMessage, orderInfo, TimeUtil.getCurrentTime());
    }

    private void bindOrderCalMessage(KafkaMessage kafkaMessage, OrderInfo orderInfo, String dealTime) {
        kafkaMessage.add(KafkaTopic.STATISTICS_CAL_SUPPLIER, orderInfo.getSupplierId().toString(), dealTime);
        kafkaMessage.add(KafkaTopic.STATISTICS_CAL_SALESMAN, orderInfo.getSalesmanId().toString(), dealTime);
        kafkaMessage.add(KafkaTopic.STATISTICS_CAL_DESTINATION, orderInfo.getDestinationId().toString(), dealTime);
    }

    private BigDecimal dealExpenses(OrderInfo orderInfo,Map<String,Object> params){
        // 运费
        BigDecimal freight = new BigDecimal(params.get("freight").toString());
        // 罚款
        BigDecimal penalty =new BigDecimal(params.get("penalty").toString());
        // 业务费
        BigDecimal expense =new BigDecimal(params.get("expense").toString());
        // 其他
        BigDecimal other =new BigDecimal(params.get("other").toString());

        OrderExpenses model =new OrderExpenses(orderInfo);
        model.setFreight(freight);
        model.setPenalty(penalty);
        model.setExpense(expense);
        model.setOther(other);
        model.setOrderPrice(orderInfo.getActualAmount());
        model.setProfit(
                orderInfo.getActualAmount()
                        .subtract(freight)
                        .subtract(penalty)
                        .subtract(expense)
                        .subtract(other).setScale(2,BigDecimal.ROUND_HALF_UP)
        );
        orderExpensesMapper.insert(model);
        return model.getProfit();
    }
}
